/*	Copyright 2007 - Xavier Baro (xbaro@cvc.uab.cat)

This file is part of eapmlib.

Eapmlib is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or any 
later version.

Eapmlib is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "Detector.h"
#include "IntImageData.h"
#include "ImageData.h"

#define _TEST_COMP

#ifdef _TEST_COMP
	#include "ImageData.h"
#endif

Evolutive::CDetector::CDetector(void):m_Classifier(NULL),m_MinScale(1.0),m_MaxScale(4.0),m_ScaleStep(1.2)
{	
#ifndef USE_OPENCV
	OPENCV_ERROR_CLASS("CDetector");
#endif
}

Evolutive::CDetector::~CDetector(void)
{		
}

#ifdef USE_OPENCV
void Evolutive::CDetector::SetClassifier(CClassifier *Classifier)
{
	m_Classifier=Classifier;
}

void Evolutive::CDetector::SetMaxScale(double Scale)
{
	m_MaxScale=Scale;
}

void Evolutive::CDetector::SetMinScale(double Scale)
{
	m_MinScale=Scale;
}

void Evolutive::CDetector::SetScaleStep(double Step)
{
	m_ScaleStep=Step;
}

void Evolutive::CDetector::FindRegions(CRectVector *Regions,IplImage *Image)
{
	FindRegions(Regions,Image,m_Classifier,m_ScaleStep,m_MinScale,m_MaxScale);
}

void Evolutive::CDetector::FindRegions(CRectVector *Regions,IplImage *Image,CClassifier *Classifier,double ScaleStep,double MinScale,double MaxScale)
{
	register int x,y;
	int Offset;
	CIntImageData *pData;
	IplImage *pIntegral=NULL,*pRIntegral=NULL,*pSqIntegral=NULL;
	CvSize IntSize;
	CvRect ROI;
	double Scale;
	CvSize WinSize;
#ifdef _TEST_COMP
	IplImage *AuxImage=NULL;
	CImageData *AuxData=NULL;
	double AuxValue,NormValue;
#endif

	// Get the window size from the classifier
	WinSize=Classifier->GetSize();

	//Check the input parameters
	if(!Regions)
		throw CEvolutiveLibException("NULL pointer to regions vector.",__FILE__,__LINE__,"FindRegions");
	if(!Classifier)
		throw CEvolutiveLibException("NULL pointer to classifier object.",__FILE__,__LINE__,"FindRegions");
	if(!Image)
		throw CEvolutiveLibException("NULL pointer to the image.",__FILE__,__LINE__,"FindRegions");
	if(WinSize.width<=0 || WinSize.height<=0)
		throw CEvolutiveLibException("Undefined classifier window size.",__FILE__,__LINE__,"FindRegions");
	if(MinScale>MaxScale)
		throw CEvolutiveLibException("Minimum detection scale cannot be larger than maximum detection scale.",__FILE__,__LINE__,"FindRegions");
	if(ScaleStep<=1.0)
		throw CEvolutiveLibException("Scale step must be larger than 1.0.",__FILE__,__LINE__,"FindRegions");

	IntSize=cvGetSize(Image);
	IntSize.height++;
	IntSize.width++;

	// Create the integral images
	pIntegral=cvCreateImage(IntSize,IPL_DEPTH_32S,1);
	pRIntegral=cvCreateImage(IntSize,IPL_DEPTH_32S,1);
	pSqIntegral=cvCreateImage(IntSize,IPL_DEPTH_64F,1);

	// Calculate the integral image
	cvIntegral(Image,pIntegral,pSqIntegral,pRIntegral);		

	// Create a new data object from the given image
	pData=new CIntImageData(pIntegral,pRIntegral);

	// Clear old regions
	Regions->clear();

	// In the following, a region of interest (ROI) is used to select the window in
	// the input image that will be analyzed. Finally, this window is classified using
	// the classifier, if the result is positive it is stored in the regions vector, if
	// not it is discarded.
	// For any allowed scale
	for(Scale=MinScale;Scale<=MaxScale;Scale*=ScaleStep)
	{
		pData->SetScale(Scale);
		WinSize.height=cvRound(WinSize.height*Scale);
		WinSize.width=cvRound(WinSize.width*Scale);
		ROI.height=WinSize.height;
		ROI.width=WinSize.width;
#ifdef _TEST_COMP
		AuxImage=cvCreateImage(WinSize,IPL_DEPTH_8U,1);
#endif
		// For any position
		for(y=0;y<Image->height-WinSize.height-1;y++)
		{
			ROI.y=y;
			for(x=0;x<Image->width-WinSize.width-1;x++)
			{
				ROI.x=x;
#ifdef _TEST_COMP		
		cvSetImageROI(Image,ROI);
		cvResize(Image,AuxImage,CV_INTER_CUBIC);
		cvResetImageROI(Image);
		AuxData=new CImageData(AuxImage);
		AuxData->SetScale(Scale);
		AuxValue=Classifier->Apply(AuxData);
		pData->SetROI(ROI);	
		NormValue=Classifier->Apply(dynamic_cast<CData*>(pData));

		if(Scale!=1.0)
		{
			int a=1;
		}

		if(NormValue!=AuxValue)
		{
			cout << "Position: (" << x << "," << y << ") Scale=" << Scale << endl;
			cout << "DEBUG STEP: " << AuxValue << endl;
			cout << "NORMAL STEP: " << NormValue << endl << endl;
		}
		
#endif
				pData->SetROI(ROI);	
				if(Classifier->Apply(dynamic_cast<CData*>(pData))>0)
				{
					// Store this region					
					Regions->push_back(ROI);
				}
			}
		}
#ifdef _TEST_COMP
		cvReleaseImage(&AuxImage);
		//delete AuxData;
#endif

	}

	// Remove the temporal data	
	cvReleaseImage(&pIntegral);
	cvReleaseImage(&pRIntegral);
	cvReleaseImage(&pSqIntegral);
	//delete pData;
}

void Evolutive::CDetector::ShowRegions(CRectVector *Regions,IplImage *Image,int WaitTime)
{
	IplImage *RegImage=NULL;

	// Create the image with the regions
	RegImage=CreateRegionsImage(Regions,Image);

	// Show the image
	cvNamedWindow("Detected Regions",0);
	cvShowImage("Detected Regions",RegImage);
	cvWaitKey(WaitTime);

	// Remove the window and the image
	cvDestroyWindow("Detected Regions");
	cvReleaseImage(&RegImage);
}

IplImage* Evolutive::CDetector::CreateRegionsImage(CRectVector *Regions,IplImage *Image)
{
	IplImage *OutImage=NULL;
	register int i;
	CvRect Rect;
	CvPoint pt1,pt2;

	// Create a color output image
	if(Image->nChannels==3)
	{
		cvCopyImage(Image,OutImage);
	}
	else
	{
		OutImage=cvCreateImage(cvGetSize(Image), 8, 3 );
		cvCvtColor(Image,OutImage,CV_GRAY2RGB);
	}

	// Draw the regions	
	cvNamedWindow("AAA",1);
	bool stop=true;
	for( i = 0; i < Regions->size(); i++ )
	{
		Rect=(*Regions)[i];		
		pt1.x=Rect.x;
		pt1.y=Rect.y;
		pt2.x=pt1.x+Rect.width;
		pt2.y=pt1.y+Rect.height;
		cvRectangle(OutImage,pt1,pt2,CV_RGB(255,0,0),1);
		
		if(stop)
		{
			cvShowImage("AAA",OutImage);
			if(cvWaitKey(0)==27)
				stop=false;
		}
	}
	cvDestroyWindow("AAA");
	return OutImage;
}

double Evolutive::CDetector::GetClass(IplImage *Image)
{
	return GetClass(Image,m_Classifier);
}

double Evolutive::CDetector::GetClass(IplImage *Image,CClassifier *Classifier)
{
	CData *pData=NULL;
	double RetVal;

	// Point the data
	pData=new CImageData(Image);

	// Apply the classifier
	RetVal=Classifier->Apply(pData);
	
	// Release the data
	delete pData;

	return RetVal;
}

void Evolutive::CDetector::FilterRegions(CRectVector *Regions,int NumNeighbors)
{
	register int i,j;

	// TODO: Remove isolated regions
	for(i=0;i<Regions->size();i++)
	{

	}
}

void Evolutive::CDetector::GroupRegions(CRectVector *Regions,REGJOIN_METHOD Method)
{
	// TODO: Group similar regions in just one
	switch(Method)
	{
	case REGJOIN_MEAN:
		break;
	case REGJOIN_BOUNDINGBOX:
		break;
	case REGJOIN_SMALLER:
		break;
	}
}

#endif //USE_OPENCV